Imports system.drawing

Namespace Overlay
    ''' <summary>
    ''' The AnalogGauge object draws a graphical representation of a instrument gauge and offers drill-down 
    ''' capability that can be used to update the value of the gauge. Many aspects of the gauge such as 
    ''' color, dial diameter and label font and color can be adjusted.
    ''' </summary>
    ''' <remarks></remarks>
    Public Class AnalogGauge
        Inherits Chart

        Protected moRegionList As ArrayList

        Private miDiameter As Integer = 200
        Private miStartAngle As Integer = 125
        Private miEndAngle As Integer = 55
        Private msnStartNumber As Single
        Private msnEndNumber As Single
        Private miNumberCount As Integer = 7
        Private msnValue As Single

        Private moFaceColor As Color = Color.DarkGray

        Private miBezelMargin As Integer = 12
        Private miBezelWidth As Integer = 12
        Private moBezelColor As Color = Color.CornflowerBlue

        Private miIndicatorBandWidth = 60

        Private miLabelRadius = 30
        Private moLabelColor As Color = Color.Black
        Private moLabelFont As Font = New System.Drawing.Font("Arial", 11, FontStyle.Bold)

        Private moValueColor As Color = Color.White
        Private moValueBoxColor As Color = Color.Black
        Private moValueFont As Font = New System.Drawing.Font("Arial", 12, FontStyle.Bold)
        Private mbValueBox As Boolean = True

        Private moNeedleColor As Color = Color.Gray
        Private moHubColor As Color = Color.LightGray

        Private miLowPercent As Single = 0.3
        Private miMedPercent As Single = 0.6
        Private miHighPercent As Single = 0.1

        Private moLowColor As Color = Color.Green
        Private moMedColor As Color = Color.Yellow
        Private moHighColor As Color = Color.Red

        Private mGauge As Bitmap
        Private mRect As Rectangle

#Region "Public"
        ''' <summary>
        ''' iWidth and iHeight specifies the initial width and height of the area on which the 
        ''' gauge will be painted, and iDiameter is the diameter of the gauge. snStartNumber and 
        ''' snEndNumber specify the starting and ending numbers of the range of numbers drawn on the gauge dial.
        ''' </summary>
        ''' <param name="iWidth"></param>
        ''' <param name="iHeight"></param>
        ''' <param name="iDiameter"></param>
        ''' <param name="snStartNumber"></param>
        ''' <param name="snEndNumber"></param>
        ''' <remarks></remarks>
        Public Sub New( _
            ByVal iWidth As Integer, _
            ByVal iHeight As Integer, _
            ByVal iDiameter As Integer, _
            ByVal snStartNumber As Single, _
            ByVal snEndNumber As Single)

            MyBase.New(iWidth, iHeight)

            miDiameter = iDiameter
            miLabelRadius = miDiameter / 2 - miIndicatorBandWidth

            msnStartNumber = snStartNumber
            msnEndNumber = snEndNumber

            moRegionList = New ArrayList
        End Sub

        ''' <summary>
        ''' The outermost diameter of the gauge, in pixels.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Diameter() As Integer
            Get
                Return miDiameter
            End Get
            Set(ByVal Value As Integer)
                miDiameter = Value
            End Set
        End Property

        ''' <summary>
        ''' The initial angle (leftmost) at which labels are displayed on the gauge face.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property StartAngle() As Integer
            Get
                Return miStartAngle
            End Get
            Set(ByVal Value As Integer)
                miStartAngle = Value
            End Set
        End Property

        ''' <summary>
        ''' The terminal angle (rightmost) at which labels are displayed on the gauge face.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property EndAngle() As Integer
            Get
                Return miEndAngle
            End Get
            Set(ByVal Value As Integer)
                miEndAngle = Value
            End Set
        End Property

        ''' <summary>
        ''' The starting (leftmost) number to be displayed.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property StartNumber() As Single
            Get
                Return msnStartNumber
            End Get
            Set(ByVal Value As Single)
                msnStartNumber = Value
            End Set
        End Property

        ''' <summary>
        ''' The ending (rightmost) number to be displayed.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property EndNumber() As Single
            Get
                Return msnEndNumber
            End Get
            Set(ByVal Value As Single)
                msnEndNumber = Value
            End Set
        End Property

        ''' <summary>
        ''' The count of numbers to display. Numbers are displayed as a graduation from StartNumber 
        ''' to EndNumber with NumberCount divisions.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property NumberCount() As Integer
            Get
                Return miNumberCount
            End Get
            Set(ByVal Value As Integer)
                miNumberCount = Value
            End Set
        End Property

        ''' <summary>
        ''' The X centerpoint of the gauge within the chart image.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property X() As Integer
            Get
                Return CenterX()
            End Get
        End Property

        ''' <summary>
        ''' The Y centerpoint of the gauge within the chart image.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Y() As Integer
            Get
                Return CenterY()
            End Get
        End Property

        ''' <summary>
        ''' The indicated value of the gauge (the value to which the gauge needle will point).
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Value() As Single
            Get
                Return msnValue
            End Get
            Set(ByVal Value As Single)
                If Value >= msnEndNumber Then
                    msnValue = msnEndNumber

                ElseIf Value <= msnStartNumber Then
                    msnValue = msnStartNumber

                Else
                    msnValue = Value
                End If
            End Set
        End Property

        ''' <summary>
        ''' True if a box should be displayed that contains the decimal value of the indicated value.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property ValueBox() As Boolean
            Get
                Return mbValueBox
            End Get
            Set(ByVal Value As Boolean)
                mbValueBox = Value
            End Set
        End Property

        ''' <summary>
        ''' The percentage of the indicator range colored using the color assigned to the HighColor property.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property HighPercent() As Single
            Get
                Return miHighPercent
            End Get
            Set(ByVal value As Single)
                miHighPercent = value
            End Set
        End Property

        ''' <summary>
        ''' The percentage of the indicator range colored using the color assigned to the MedColor property.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property MedPercent() As Single
            Get
                Return miMedPercent
            End Get
            Set(ByVal value As Single)
                miMedPercent = value
            End Set
        End Property

        ''' <summary>
        ''' The percentage of the indicator range colored using the color assigned to the LowColor property.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property LowPercent() As Single
            Get
                Return miLowPercent
            End Get
            Set(ByVal value As Single)
                miLowPercent = value
            End Set
        End Property

        ''' <summary>
        ''' The color to use for the rightmost indicator color band.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property HighColor() As Color
            Get
                Return moHighColor
            End Get
            Set(ByVal value As Color)
                moHighColor = value
            End Set
        End Property

        ''' <summary>
        ''' The color to use for the midrange indicator color band.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property MedColor() As Color
            Get
                Return moMedColor
            End Get
            Set(ByVal value As Color)
                moMedColor = value
            End Set
        End Property

        ''' <summary>
        ''' The color to use for the leftmost indicator color band.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property LowColor() As Color
            Get
                Return moLowColor
            End Get
            Set(ByVal value As Color)
                moLowColor = value
            End Set
        End Property

        ''' <summary>
        ''' The color to use for the leftmost indicator color band.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property FaceColor() As Color
            Get
                Return moFaceColor
            End Get
            Set(ByVal value As Color)
                moFaceColor = value
            End Set
        End Property

        ''' <summary>
        ''' The color to use for the outer bezel area.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property BezelColor() As Color
            Get
                Return moBezelColor
            End Get
            Set(ByVal value As Color)
                moBezelColor = value
            End Set
        End Property

        ''' <summary>
        ''' The color to use for the value labels as displayed around the outer rim.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property LabelColor() As Color
            Get
                Return moLabelColor
            End Get
            Set(ByVal value As Color)
                moLabelColor = value
            End Set
        End Property

        ''' <summary>
        ''' The color to use for the value as displayed in the value box area.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property ValueColor() As Color
            Get
                Return moValueColor
            End Get
            Set(ByVal value As Color)
                moValueColor = value
            End Set
        End Property

        ''' <summary>
        ''' The background color to use for the value box area.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property ValueBoxColor() As Color
            Get
                Return moValueBoxColor
            End Get
            Set(ByVal value As Color)
                moValueBoxColor = value
            End Set
        End Property

        ''' <summary>
        ''' The color to use for the indicator needle.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property NeedleColor() As Color
            Get
                Return moNeedleColor
            End Get
            Set(ByVal value As Color)
                moNeedleColor = value
            End Set
        End Property

        ''' <summary>
        ''' The color to use for the indicator needle hub.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property HubColor() As Color
            Get
                Return moHubColor
            End Get
            Set(ByVal value As Color)
                moHubColor = value
            End Set
        End Property

        ''' <summary>
        ''' The font to use for the value labels displayed around the outer rim.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property LabelFont() As Font
            Get
                Return moLabelFont
            End Get
            Set(ByVal value As Font)
                moLabelFont = value
            End Set
        End Property

        ''' <summary>
        ''' The font to use for the value displayed in the value box.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property ValueFont() As Font
            Get
                Return moValueFont
            End Get
            Set(ByVal value As Font)
                moValueFont = value
            End Set
        End Property
#End Region

#Region "Bezel"
        Private Sub DrawBezel(ByRef g As Graphics)
            mRect = New Rectangle(CenterX() - (miDiameter / 2), CenterY() - (miDiameter / 2), miDiameter + 1, miDiameter + 1)

            MakeOutsizeBezel(g)
            MakeIndicatorLabels(g)
            MakeFacePlate(g)

            DrawNumbers(g)
        End Sub

        Private Sub MakeOutsizeBezel( _
            ByRef e As Graphics)

            Dim BevelBrush As New SolidBrush(Color.FromArgb(210, moBezelColor.R, moBezelColor.G, moBezelColor.B))
            Dim FaceBrush As New SolidBrush(moFaceColor)

            Dim OuterRect As Rectangle = MakeCenteredRect(miDiameter)
            Dim InnerRect As Rectangle = MakeCenteredRect(miDiameter - miBezelMargin)

            e.FillEllipse(BevelBrush, OuterRect)
            e.DrawArc(New Pen(moBezelColor), OuterRect, 0, 360)

            e.FillEllipse(FaceBrush, InnerRect)
            e.DrawArc(New Pen(moBezelColor), InnerRect, 0, 360)

            BevelBrush.Dispose()
            FaceBrush.Dispose()
        End Sub

        Private Sub MakeIndicatorLabels( _
            ByRef e As Graphics)
            Dim LowBrush As New SolidBrush(moLowColor)
            Dim MedBrush As New SolidBrush(moMedColor)
            Dim HighBrush As New SolidBrush(moHighColor)

            Dim LabelRect As Rectangle = MakeCenteredRect(miDiameter - (miBezelWidth + miBezelMargin))
            Dim InnerLabelRect As Rectangle = MakeCenteredRect(miDiameter - (miBezelWidth + miBezelMargin + miIndicatorBandWidth))

            Dim iFullRange As Integer = (360 - miStartAngle) + miEndAngle
            Dim iLowRange = iFullRange * miLowPercent
            Dim iMedRange = iFullRange * miMedPercent
            Dim iHighRange = iFullRange * miHighPercent

            '---Draw low indicators
            DrawIndicatorRange(e, LabelRect, InnerLabelRect, miStartAngle, iLowRange, LowBrush)

            '---Draw medium indicators
            DrawIndicatorRange(e, LabelRect, InnerLabelRect, AdjustedAngle(miStartAngle + iLowRange) + 4, iMedRange - 4, MedBrush)

            '---Draw high indicators
            DrawIndicatorRange(e, LabelRect, InnerLabelRect, AdjustedAngle(miStartAngle + iLowRange + iMedRange) + 8, iHighRange - 8, HighBrush)

            LowBrush.Dispose()
            MedBrush.Dispose()
            HighBrush.Dispose()
        End Sub

        Private Function AdjustedAngle(ByVal iAngle As Integer) As Integer
            If iAngle > 360 Then
                Return iAngle - 360
            Else
                Return iAngle
            End If
        End Function

        Private Sub DrawNumbers( _
            ByRef e As Graphics)

            Dim iNLX As Integer
            Dim iNLY As Integer

            Dim iCurNumber As Integer = msnStartNumber
            Dim snCurAngle As Single
            Dim iPosAngle As Integer

            Dim iAngleRange As Integer = (360 - miStartAngle) + miEndAngle
            Dim iNumberInc As Integer = (msnEndNumber - msnStartNumber) / miNumberCount
            Dim snAngleInc As Single = iAngleRange / miNumberCount

            snCurAngle = 0

            Do While snCurAngle <= iAngleRange
                If miStartAngle + snCurAngle > 360 Then
                    iPosAngle = (miStartAngle + snCurAngle) - 360
                Else
                    iPosAngle = miStartAngle + snCurAngle
                End If

                Dim oSize As SizeF = e.MeasureString(iCurNumber, moLabelFont)

                iNLX = CInt(CDbl(miLabelRadius + oSize.Height - 5) * Math.Cos(AngleToRadian(iPosAngle)))
                iNLY = CInt(CDbl(miLabelRadius + oSize.Height - 5) * Math.Sin(AngleToRadian(iPosAngle)))

                CenterNumber( _
                        e, _
                        iCurNumber, _
                        CenterX(), _
                        CenterY(), _
                        iNLX, _
                        iNLY)

                iCurNumber = iCurNumber + iNumberInc
                snCurAngle = snCurAngle + snAngleInc
            Loop

        End Sub

        Private Function AngleToRadian( _
            ByVal iAngle As Integer) As Double
            Dim dAngleToRadian As Double = iAngle * 2.0 * Math.PI / 360.0

            Return dAngleToRadian
        End Function

        Private Sub CenterNumber( _
            ByRef e As Graphics, _
            ByVal iNumber As Integer, _
            ByVal iX As Integer, _
            ByVal iY As Integer, _
            ByVal iOffSetX As Integer, _
            ByVal iOffSetY As Integer)

            Dim LetterBrush As New SolidBrush(moLabelColor)
            Dim drawFormat As New System.Drawing.StringFormat

            Dim sNumber As String = iNumber.ToString
            Dim oSize As SizeF = e.MeasureString(sNumber, moLabelFont)

            Dim iAdjustedX As Integer
            Dim iAdjustedY As Integer

            iAdjustedX = iX + iOffSetX - (oSize.Width / 2)
            iAdjustedY = iY + iOffSetY - (oSize.Height / 2)

            e.DrawString( _
                sNumber, _
                moLabelFont, _
                LetterBrush, _
                iAdjustedX, _
                iAdjustedY, _
                drawFormat)

            LetterBrush.Dispose()
            drawFormat.Dispose()
        End Sub

        Private Sub DrawIndicatorRange( _
            ByRef e As Graphics, _
            ByVal rLabelRect As Rectangle, _
            ByVal rInnerLabelRect As Rectangle, _
            ByVal iStartAngle As Integer, _
            ByVal iAngleRange As Integer, _
            ByVal rBrush As Brush)

            '---Draw indicators
            Dim iPos As Integer

            For iPos = iStartAngle To iStartAngle + iAngleRange Step 22
                Dim iWedgeSize As Integer = 20
                If (iPos + iWedgeSize) > (iStartAngle + iAngleRange) Then
                    iWedgeSize = (iStartAngle + iAngleRange) - iPos
                End If

                e.FillPie(rBrush, rLabelRect, iPos, iWedgeSize)
            Next iPos

            e.FillPie(rBrush, rInnerLabelRect, iStartAngle, iAngleRange)
        End Sub

        Private Sub MakeFacePlate( _
            ByRef e As Graphics)
            Dim FaceBrush As New SolidBrush(moFaceColor)

            Dim InnerRect As Rectangle = MakeCenteredRect(miDiameter - (miBezelWidth + miIndicatorBandWidth))

            e.FillEllipse(FaceBrush, InnerRect)

            FaceBrush.Dispose()
        End Sub

        Private Function MakeCenteredRect( _
            ByVal iDiameter As Integer) As Rectangle

            Dim rCenteredRect As Rectangle = New Rectangle(CenterX() - (iDiameter / 2), CenterY() - (iDiameter / 2), iDiameter, iDiameter)

            Return rCenteredRect
        End Function
#End Region

#Region "Values"
        Private Sub DrawNeedle( _
            ByRef e As Graphics)

            Dim iRadius As Integer = (miDiameter - (miBezelWidth + miIndicatorBandWidth / 2)) / 2
            Dim iNLX As Integer
            Dim iNLY As Integer

            Dim iPosAngle As Integer

            Dim iAngleRange As Integer = (360 - miStartAngle) + miEndAngle

            Dim snNumberInc As Single = iAngleRange / (msnEndNumber - msnStartNumber)

            Dim snNeedleAngle As Integer = (msnValue - msnStartNumber) * snNumberInc + miStartAngle

            Dim PointerBrush As New SolidBrush(Color.FromArgb(210, moNeedleColor.R, moNeedleColor.G, moNeedleColor.B))
            Dim HubBrush As New SolidBrush(Color.FromArgb(210, moHubColor.R, moHubColor.G, moHubColor.B))

            If snNeedleAngle > 360 Then
                iPosAngle = snNeedleAngle
            Else
                iPosAngle = snNeedleAngle
            End If

            '---Draw pointer
            Dim NeedlePoints(4) As System.Drawing.PointF

            '---Needle center
            iNLX = 6 * Math.Cos(AngleToRadian(iPosAngle - 90))
            iNLY = 6 * Math.Sin(AngleToRadian(iPosAngle - 90))
            NeedlePoints(0).X = CenterX() + iNLX
            NeedlePoints(0).Y = CenterY() + iNLY

            '---Needle edge 1
            iNLX = CInt(CDbl(iRadius * 0.75) * Math.Cos(AngleToRadian(iPosAngle - 12)))
            iNLY = CInt(CDbl(iRadius * 0.75) * Math.Sin(AngleToRadian(iPosAngle - 12)))
            NeedlePoints(1).X = CenterX() + iNLX
            NeedlePoints(1).Y = CenterY() + iNLY

            '---Needle end
            iNLX = CInt(CDbl(iRadius * 1) * Math.Cos(AngleToRadian(iPosAngle)))
            iNLY = CInt(CDbl(iRadius * 1) * Math.Sin(AngleToRadian(iPosAngle)))
            NeedlePoints(2).X = CenterX() + iNLX
            NeedlePoints(2).Y = CenterY() + iNLY

            '---Needle edge 2
            iNLX = CInt(CDbl(iRadius * 0.75) * Math.Cos(AngleToRadian(iPosAngle + 12)))
            iNLY = CInt(CDbl(iRadius * 0.75) * Math.Sin(AngleToRadian(iPosAngle + 12)))
            NeedlePoints(3).X = CenterX() + iNLX
            NeedlePoints(3).Y = CenterY() + iNLY

            '---Needle center
            iNLX = 6 * Math.Cos(AngleToRadian(iPosAngle + 90))
            iNLY = 6 * Math.Sin(AngleToRadian(iPosAngle + 90))
            NeedlePoints(4).X = CenterX() + iNLX
            NeedlePoints(4).Y = CenterY() + iNLY

            e.FillPolygon(New SolidBrush(Color.Black), NeedlePoints)
            e.FillPolygon(PointerBrush, NeedlePoints)

            e.DrawLine(New Pen(moNeedleColor), CenterX(), CenterY(), NeedlePoints(2).X, NeedlePoints(2).Y)

            '---Draw hub
            Dim rHubRect As New Rectangle(CenterX() - 10, CenterY() - 10, 20, 20)
            e.FillEllipse(New SolidBrush(Color.Black), rHubRect)
            e.FillEllipse(HubBrush, rHubRect)

            rHubRect = New Rectangle(CenterX() - 8, CenterY() - 8, 16, 16)
            e.DrawArc(New Pen(moHubColor), rHubRect, 0, 360)

            PointerBrush.Dispose()
        End Sub

        Private Sub DrawValue( _
            ByRef e As Graphics)

            Dim LetterBrush As New SolidBrush(moValueColor)
            Dim drawFormat As New System.Drawing.StringFormat

            Dim sNumber As String = msnEndNumber.ToString
            Dim oSize As SizeF = e.MeasureString("A", moValueFont)

            Dim iTotalWordSize = (oSize.Width + 2) * msnEndNumber.ToString.Length

            Dim iAdjustedX As Integer = CenterX() - (iTotalWordSize / 2)
            Dim iAdjustedY As Integer = CenterY() + (miDiameter / 2) - oSize.Height - 15

            Dim rShellRect As New Rectangle(iAdjustedX - 4, iAdjustedY - 1, (msnEndNumber.ToString.Length * oSize.Width + 4) + 5, oSize.Height + 4)

            Dim iCharX As Integer
            Dim iCharY As Integer

            '---Draw odometer shell
            iCharY = iAdjustedY + 1

            If mbValueBox = True Then
                e.FillRectangle(New SolidBrush(moValueBoxColor), rShellRect)
            End If

            '---Draw odometer numbers

            Dim sChar As String
            Dim iOffset As Integer = msnEndNumber.ToString.Length - msnValue.ToString.Length

            iCharY = iAdjustedY + 1

            For iCurLetter As Integer = 0 To msnValue.ToString.Length - 1
                sChar = msnValue.ToString.Substring(iCurLetter, 1)

                iCharX = (iAdjustedX) + ((iCurLetter + iOffset) * (oSize.Width + 2))

                e.DrawString( _
                    sChar, _
                    moValueFont, _
                    LetterBrush, _
                    iCharX, _
                    iCharY, _
                    drawFormat)
            Next

            LetterBrush.Dispose()
            drawFormat.Dispose()
        End Sub
#End Region

        Private Sub AddRegions()
            Dim iPosAngle As Integer
            Dim iAngleRange As Integer = (360 - miStartAngle) + miEndAngle
            Dim snValueIncrement As Single = (msnEndNumber - msnStartNumber) / iAngleRange

            For iAngle As Integer = 0 To iAngleRange
                If (miStartAngle + iAngle) > 360 Then
                    iPosAngle = (miStartAngle + iAngle) - 360
                Else
                    iPosAngle = miStartAngle + iAngle
                End If

                Dim oSlice(2) As Point
                oSlice(0).X = CenterX()
                oSlice(0).Y = CenterY()

                oSlice(1).X = CenterX() + CInt(CDbl(miDiameter / 2) * Math.Cos(AngleToRadian(iPosAngle - 0.5)))
                oSlice(1).Y = CenterY() + CInt(CDbl(miDiameter / 2) * Math.Sin(AngleToRadian(iPosAngle - 0.5)))

                oSlice(2).X = CenterX() + CInt(CDbl(miDiameter / 2) * Math.Cos(AngleToRadian(iPosAngle + 0.5)))
                oSlice(2).Y = CenterY() + CInt(CDbl(miDiameter / 2) * Math.Sin(AngleToRadian(iPosAngle + 0.5)))

                Dim snValue As Single = msnStartNumber + (iAngle * snValueIncrement)

                Dim oRegionArea As New RegionArea("Gauge", 0, snValue, oSlice)
                moRegionList.Add(oRegionArea)
            Next
        End Sub

        ''' <summary>
        ''' Draws all charts belonging to the OverLay on a System.Drawing.Graphics object.
        ''' </summary>
        ''' <param name="g"></param>
        ''' <remarks></remarks>
        Public Overrides Sub DrawChart(ByVal g As System.Drawing.Graphics)
            moChartDimensions.DrawBackground(g)

            DrawBezel(g)
            DrawValue(g)
            DrawNeedle(g)
            AddRegions()
        End Sub

        Private Function CenterX() As Integer
            Return MyBase.ChartDimensions.InnerLeft + MyBase.ChartDimensions.InnerWidth / 2
        End Function

        Private Function CenterY() As Integer
            Return MyBase.ChartDimensions.InnerTop + MyBase.ChartDimensions.InnerHeight / 2
        End Function

        ''' <summary>
        ''' An ArrayList containing jChart.RegionArea objects for each label displayed on the chart dial.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property RegionList() As ArrayList
            Get
                Return moRegionList
            End Get
        End Property
    End Class
End Namespace